package com.haoji.upload.common.aop.log;

import com.alibaba.fastjson.JSON;
import com.haoji.upload.common.dto.SysLogsDTO;
import com.haoji.upload.common.exception.BaseException;
import com.haoji.upload.common.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.sql.Timestamp;

/**
 * @program: upload
 * @description:
 * @author: xiaolei
 * @create: 2021-04-16 16:47
 **/

@Slf4j
@Aspect
@Component
public class SysLogAspect {

    private static final String PASSWORD = "password";
    private static final String PWD = "pwd";

    //定义切点,切面
    @Pointcut("@annotation(com.haoji.upload.common.aop.log.SysLogger)")
    public void controller(){}

    /**
     * 操作异常记录
     * @descript
     * @param point
     * @version 1.0
     * @throws Exception
     */
    @AfterThrowing(pointcut = "controller()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) throws ClassNotFoundException {
        SysLogsDTO sysLogsDTO = getControllerMethodDescription(point);
        String msg = ExceptionUtils.getStackTrace(e);
        sysLogsDTO.setResult(msg.length() > 1000 ? msg.substring(0, 999) : msg);
        log.error(String.format("用户操作异常:{}", sysLogsDTO.toString()));
        log.error(msg);
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     * @param joinPoint 切点
     * @throws Exception
     * @throws BaseException
     */
    @Around("controller()")
    public Object doController(ProceedingJoinPoint point) throws ClassNotFoundException {
        Object result = null;
        // 执行方法名
        Long start = 0L;
        Long end = 0L;
        Long spendTime = 0L;
        SysLogsDTO sysLogsDTO = getControllerMethodDescription(point);
        // 执行方法所消耗的时间
        start = System.currentTimeMillis();
        try {
            result = point.proceed();
        } catch (Throwable e) {
            if (StringUtils.isBlank(e.getMessage())) {
                throw new BaseException(e);
            } else {
                throw new BaseException(e.getMessage());
            }
        }
        sysLogsDTO.setResult(JSON.toJSONString(result));
        end = System.currentTimeMillis();
        spendTime = end - start;
        sysLogsDTO.setResult(JSON.toJSONString(result));
        sysLogsDTO.setSpendTime(spendTime);
        log.info(String.format("用户操作日志:%s", sysLogsDTO.toString()));
        return result;
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * @param joinPoint 切点
     * @return 方法描述
     * @throws ClassNotFoundException
     * @throws Exception
     */
    public SysLogsDTO getControllerMethodDescription(JoinPoint joinPoint) throws ClassNotFoundException {
        SysLogsDTO sysLogsDTO = new SysLogsDTO();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        String uri = request.getRequestURI();
        String userId = "UID";
        sysLogsDTO.setType(method.getAnnotation(SysLogger.class).type());
        StringBuilder sb = new StringBuilder();
        sb.append(className).append(".").append(methodName);
        sysLogsDTO.setMethod(sb.toString());
        sysLogsDTO.setParam(getParams(joinPoint));
        sysLogsDTO.setRequestUri(uri);
        sysLogsDTO.setUserId(userId);
        String ipAddr = IpUtils.getIpAddr(request);
        sysLogsDTO.setIpAddr(ipAddr);
        Timestamp time = new Timestamp(System.currentTimeMillis());
        sysLogsDTO.setCreateTime(time);
        sysLogsDTO.setUpdateTime(time);
        return sysLogsDTO;
    }

    /**
     * 只获取非文件、非request、非密码的字段参数
     * @param point
     * @return
     * @throws ClassNotFoundException
     */
    private String getParams(JoinPoint point) throws ClassNotFoundException {
        String[] parameterNames = ((MethodSignature)point.getSignature()).getParameterNames();
        StringBuilder sb = null;
        if (ArrayUtils.isNotEmpty(parameterNames)) {
            sb = new StringBuilder();
            for (int i = 0; i < parameterNames.length; i++) {
                Object object = point.getArgs()[i];
                if (StringUtils.isNotBlank(parameterNames[i]) && object != null
                        && !(object instanceof HttpServletRequest || object instanceof MultipartFile)
                        && (parameterNames[i].toLowerCase().indexOf(PASSWORD) == -1
                        && parameterNames[i].toLowerCase().indexOf(PWD) == -1)) {
                    sb.append(parameterNames[i] + ":" + JSON.toJSONString(point.getArgs()[i]) + "; ");
                }
            }
        }
        sb = sb == null ? new StringBuilder() : sb;
        return sb.toString();
    }

}
